package fun.easycode.webterminal.common.util;

import com.jcraft.jsch.ChannelShell;
import fun.easycode.webterminal.common.jsch.JschJumpSession;
import fun.easycode.webterminal.common.jsch.JschJumpSessionPool;
import fun.easycode.webterminal.dao.Machine;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;

@Slf4j
public final class LinuxUtil {

    public static List<DiskInfo> getDiskInfo(Machine machine) {
        return exec(machine, "df", LinuxUtil::resolveDiskInfo, Collections.EMPTY_LIST);
    }


    private static List<DiskInfo> resolveDiskInfo(String readDiskStr){

        List<String> lines = Arrays.stream(readDiskStr.split("\n"))
                .filter(line -> !Objects.equals(line.trim(), "")).toList();

        List<DiskInfo> diskInfos = new ArrayList<>();

        for (int i = 1; i < lines.size(); i++) {
            String line = lines.get(i);
            String[] lineArr = line.split(" +");
            DiskInfo diskInfo = new DiskInfo();
            diskInfo.setFilesystem(lineArr[0]);
            diskInfo.setSize(Long.parseLong(lineArr[1]));
            diskInfo.setUsed(Long.parseLong(lineArr[2]));
            diskInfo.setAvailable(Long.parseLong(lineArr[3]));
            diskInfo.setUse(Integer.parseInt(lineArr[4].replace("%", "")));
            diskInfo.setMount(lineArr[5]);
            diskInfos.add(diskInfo);
        }

        return diskInfos;
    }

    public static MemInfo getMemInfo(Machine machine) {
        return exec(machine, "free", LinuxUtil::resolveMemInfo, null);
    }

    public static int getMemUsedPercent(MemInfo memInfo) {
        double memUse = Math.ceil(100 * ((double)(memInfo.getMemTotal() - memInfo.getMemFree() - memInfo.getMemBuffers()) / memInfo.getMemTotal()));

        return (int) memUse;
    }

    private static MemInfo resolveMemInfo(String readMemStr){
        String[] lines = readMemStr.split("\n");
        MemInfo memInfo = new MemInfo();
        for (int i = 1; i < lines.length; i++) {
            String[] lineArr = lines[i].split(" +");
            if(lineArr[0].startsWith("Mem")){
                memInfo.setMemTotal(Long.parseLong(lineArr[1]));
                memInfo.setMemUsed(Long.parseLong(lineArr[2]));
                memInfo.setMemFree(Long.parseLong(lineArr[3]));
                memInfo.setMemShared(Long.parseLong(lineArr[4]));
                memInfo.setMemBuffers(Long.parseLong(lineArr[5]));
                memInfo.setMemAvailable(Long.parseLong(lineArr[6]));
            }
            if(lineArr[0].startsWith("Swap")){
                memInfo.setSwapTotal(Long.parseLong(lineArr[1]));
                memInfo.setSwapUsed(Long.parseLong(lineArr[2]));
                memInfo.setSwapFree(Long.parseLong(lineArr[3]));
            }
        }
        return memInfo;
    }

    /**
     * 执行命令 返回文本执行结果
     * @param machine 机器信息
     * @param cmd 命令
     * @return 文本结果
     */
    public static String execStr(Machine machine, String cmd) {
        return exec(machine, cmd, Function.identity(), "");
    }

    public static void execHandler(Machine machine, LinuxExecHandler handler) {
        JschJumpSession jschJumpSession = null;
        ChannelShell channel = null;
        try {

            jschJumpSession = JschJumpSessionPool.INSTANCE.get(machine.getId());
//
//            sshSession = jsch.getSession(machine.getUsername(), machine.getIp(), machine.getPort());
//            sshSession.setPassword(machine.getPassword());
//            sshSession.setConfig("StrictHostKeyChecking", "no");
//            sshSession.connect();

            channel = (ChannelShell) jschJumpSession.getSession().openChannel("shell");
            channel.setPty(true);
            InputStream inputStream = channel.getInputStream();
            OutputStream outputStream = channel.getOutputStream();
            channel.connect();

            // 等待认证通过
            log.info("wait login");


            Thread thread = new Thread(() -> {
                try {
                    waitLogin(inputStream, outputStream);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });

            thread.start();

            thread.join(15000);


            log.info("login success");
//            String result = readCmdResult(cmd, inputStream, outputStream);

            handler.handle(inputStream, outputStream);

            log.info("execHandler end...");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(channel != null) {
                channel.disconnect();
            }
            if(jschJumpSession != null) {
                jschJumpSession.disconnect();
            }
        }
    }


    private static <R> R exec(Machine machine, String cmd, Function<String,R> resolve, R defaultR){
        JschJumpSession jschJumpSession = null;
        ChannelShell channel = null;
        try {

            jschJumpSession = JschJumpSessionPool.INSTANCE.get(machine.getId());

            channel = (ChannelShell) jschJumpSession.getSession().openChannel("shell");
            channel.setPty(true);
            InputStream inputStream = channel.getInputStream();
            OutputStream outputStream = channel.getOutputStream();
            channel.connect();

            // 等待认证通过
            log.info("wait login");
            waitLogin(inputStream, outputStream);
            log.info("login success");
            String result = readCmdResult(cmd, inputStream, outputStream);
            log.info("exec result: \n {}", result);
            return resolve.apply(result);
        }catch (Exception e){
            e.printStackTrace();

            return defaultR;
        }finally {
            if(channel != null) {
                channel.disconnect();
            }
            if(jschJumpSession != null) {
                jschJumpSession.disconnect();
            }
        }

    }



    private static void waitLogin(InputStream inputStream, OutputStream outputStream) throws IOException {
        outputStream.write(("echo \"web-terminal$(expr 1 + 1)\""+"\n").getBytes());
//        outputStream.flush();
        byte[] buffer = new byte[1024];
        int read;
        // 等待认证通过
        StringBuilder result = new StringBuilder();
        while ((read = inputStream.read(buffer)) != -1) {


//            outputStream.write(("echo \"web-terminal$(expr 1 + 1)\""+"\n").getBytes());
//            outputStream.flush();

            String output = new String(buffer, 0, read, StandardCharsets.UTF_8);

//            System.err.println("output => " + output);
            result.append(output);
            if(output.contains("y/n")){
                outputStream.write(("y"+"\n").getBytes());
//                outputStream.flush();
            }


            if(output.contains("@")){
//                outputStream.write(("echo \"web-terminal$(expr 1 + 1)\""+"\n").getBytes());
                outputStream.flush();
            }

            if (result.lastIndexOf("web-terminal2") != -1) {
                break;
            }
        }
//        outputStream.write(("\n").getBytes());
        outputStream.flush();
    }

    @SneakyThrows
    public static String readCmdResult(String cmd, InputStream inputStream, OutputStream outputStream) {
        PrintStream ps = new PrintStream(outputStream);

        ps.println(cmd + " && " + "echo \"web-terminal$(expr 1 + 1)\"");
        ps.flush();

//        outputStream.write((cmd+"\n").getBytes());
//        outputStream.flush();
//        outputStream.write(("echo \"web-terminal$(expr 1 + 1)\"\n").getBytes());
//        outputStream.flush();

        byte[] buffer = new byte[1024];
        int read;


        StringBuilder terminalOutput = new StringBuilder();

        while ((read = inputStream.read(buffer)) != -1) {
            String output = new String(buffer, 0, read, StandardCharsets.UTF_8);
            terminalOutput.append(output);
            if (output.contains("web-terminal2")) {
                break;
            }
        }

        log.info("terminalOutput: \n {}", terminalOutput);

        List<String> lines = Arrays.stream(terminalOutput.toString().split("\n")).filter(line -> !line.contains("echo \"web-terminal$(expr 1 + 1)")).toList();
        List<String> result = new ArrayList<>();

        for(String line : lines){
            line = line.replace("\r","");
            if(line.contains("web-terminal$(expr 1  + 1)")){
                continue;
            }
            // 如果line最后有\r则移除
//            if(line.endsWith("\r")){
//                line = line.substring(0, line.length()-1);
//            }
//            if(line.contains("\r")){
//                line = line.substring(line.indexOf("\r") + 1);
//            }
            // 跳过空行
            if(Objects.equals(line.trim(), "")){
                continue;
            }

            // 结束
            if (line.contains("web-terminal2")) {
                break;
            }
            result.add(line);
        }

        return String.join("\n", result);
    }

}
